#include "app.h"
#include <sstream>
#include <algorithm>
#include <cmath>
#include <random>

void App::init(HWND h, int w, int hgt){ hwnd=h; pp.eps=1e-5; pp.dt=baseDt; reset(); r.init(hwnd,w,hgt); energy0=total_energy(s,pp); }

void App::reset(){ if(preset==Preset::FigureEight) reset_figure_eight(s); else if(preset==Preset::FigureEightLoose) reset_figure_eight(s); else reset_solar(s);
    if(preset==Preset::FigureEightLoose){ double cs=std::cos(looseTheta), sn=std::sin(looseTheta); for(int i=0;i<3;++i){ double x=s.b[i].p.x, y=s.b[i].p.y; s.b[i].p.x = loosePosScale*(cs*x - sn*y); s.b[i].p.y = loosePosScale*(sn*x + cs*y); double vx=s.b[i].v.x, vy=s.b[i].v.y; s.b[i].v.x = looseVelScale*(cs*vx - sn*vy); s.b[i].v.y = looseVelScale*(sn*vx + cs*vy); } }
    std::mt19937 gen(rngSeed); std::uniform_real_distribution<double> up(-posRand,posRand), uv(-velRand,velRand);
    for(int i=0;i<3;++i){ s.b[i].p.x += up(gen); s.b[i].p.y += up(gen); s.b[i].v.x += uv(gen); s.b[i].v.y += uv(gen); }
    double M=0; Vec2 P{0,0}; for(int i=0;i<3;++i){ M+=s.b[i].m; P.x+=s.b[i].m*s.b[i].v.x; P.y+=s.b[i].m*s.b[i].v.y; }
    Vec2 vcm{P.x/M, P.y/M}; for(int i=0;i<3;++i){ s.b[i].v.x-=vcm.x; s.b[i].v.y-=vcm.y; }
    for(int i=0;i<3;++i){ trails[i].pts.clear(); trails[i].color = i==0?RGB(255,120,120): i==1?RGB(120,255,120):RGB(120,120,255); }
    simTime=0.0; energy0=total_energy(s,pp);
}

void App::update(double dt){ if(paused) return; accumulator += dt; int steps=0; 
    double M=0; Vec2 C{0,0}; for(int i=0;i<3;++i){ M+=s.b[i].m; C.x+=s.b[i].m*s.b[i].p.x; C.y+=s.b[i].m*s.b[i].p.y; } C=C*(1.0/M);
    double minR=1e9; for(int i=0;i<3;++i){ for(int j=i+1;j<3;++j){ Vec2 d = s.b[j].p - s.b[i].p; double r2=d.x*d.x+d.y*d.y; if(r2<minR) minR=r2; } } minR=std::sqrt(minR);
    while(steps < maxStepsPerFrame){ double scale=1.0; if(adaptiveDt){ scale = std::max(dtMinScale, std::min(1.0, minR/minRRef)); }
        double step = baseDt*scale; if(accumulator < step) break; pp.dt=step; integrate_rk4(s,pp); accumulator -= step; ++steps; simTime += step; }
    ++tick; for(int i=0;i<3;++i){ if(tick % trailSkip!=0) continue; POINT p = to_screen(s.b[i].p,view); const auto& pts = trails[i].pts; if(!pts.empty()){ POINT last = pts.back(); int dx = p.x - last.x; int dy = p.y - last.y; if(dx*dx + dy*dy < 1) continue; }
        trails[i].pts.push_back(p); if((int)trails[i].pts.size()>trailMax){ trails[i].pts.erase(trails[i].pts.begin(), trails[i].pts.begin()+ (trailMax/4)); } }
    if(autoScale){ double maxr=0; for(int i=0;i<3;++i){ Vec2 d = s.b[i].p - C; double r2=d.x*d.x+d.y*d.y; if(r2>maxr) maxr=r2; } maxr=std::sqrt(maxr); double target = autoScaleMargin*std::min(r.width(), r.height()); double pixels = maxr*view.scale; if(pixels>target*1.05){ view.scale *= 0.98; clear_trails(); } else if(pixels<target*0.65){ view.scale *= 1.02; clear_trails(); } }
    double maxSep=0; for(int i=0;i<3;++i){ for(int j=i+1;j<3;++j){ Vec2 d=s.b[j].p - s.b[i].p; double r2=d.x*d.x+d.y*d.y; if(r2>maxSep) maxSep=r2; } } maxSep=std::sqrt(maxSep);
    if(autoRandomize && simTime>=reRandInterval && maxSep>farThreshold){ rngSeed = rngSeed*1664525u + 1013904223u; reset(); }
}

void App::render(){
    if(followCOM){ double M=0; Vec2 C{0,0}; for(int i=0;i<3;++i){ M+=s.b[i].m; C.x+=s.b[i].m*s.b[i].p.x; C.y+=s.b[i].m*s.b[i].p.y; } C = C*(1.0/M); view.ox = r.width()*0.5 - C.x*view.scale; view.oy = r.height()*0.5 - C.y*view.scale; }
    r.begin(); if(showTrail){ for(int i=0;i<3;++i) r.draw_trail(trails[i]); }
    r.draw_body(s.b[0],view,RGB(255,100,100)); r.draw_body(s.b[1],view,RGB(100,255,100)); r.draw_body(s.b[2],view,RGB(100,100,255));
    double e=total_energy(s,pp); std::wstringstream ss; ss.precision(6); ss<<L"Erel="<< (energy0!=0? (e-energy0)/energy0:0.0) << L"  scale="<<view.scale; if(preset==Preset::FigureEightLoose){ ss<<L"  loose(θ="<<looseTheta<<L", ps="<<loosePosScale<<L", vs="<<looseVelScale<<L")"; } if(adaptiveDt){ ss<<L"  dt~"<<pp.dt; } ss<<L"  rand(p="<<posRand<<L", v="<<velRand<<L")"; r.draw_text(10,10,ss.str().c_str());
    r.end(hwnd);
}

void App::on_size(int w, int hgt){ r.resize(w,hgt); }

void App::on_key(WPARAM k){
    if(k==' '){ paused=!paused; }
    else if(k=='R'){ reset(); }
    else if(k==VK_OEM_PLUS || k==VK_ADD){ view.scale*=1.1; clear_trails(); }
    else if(k==VK_OEM_MINUS || k==VK_SUBTRACT){ view.scale/=1.1; clear_trails(); }
    else if(k=='C'){ showTrail=!showTrail; }
    else if(k=='1'){ preset=Preset::FigureEight; reset(); }
    else if(k=='2'){ preset=Preset::Solar; reset(); }
    else if(k=='3'){ preset=Preset::FigureEightLoose; reset(); }
    else if(k=='F'){ followCOM=!followCOM; }
    else if(k=='Z'){ double M=0; Vec2 C{0,0}; for(int i=0;i<3;++i){ M+=s.b[i].m; C.x+=s.b[i].m*s.b[i].p.x; C.y+=s.b[i].m*s.b[i].p.y; } C=C*(1.0/M); double maxr=0; for(int i=0;i<3;++i){ Vec2 d = s.b[i].p - C; double r2=d.x*d.x+d.y*d.y; if(r2>maxr) maxr=r2; } maxr=std::sqrt(maxr); if(maxr<1e-6) maxr=1e-6; double margin=0.45; double target = margin*std::min(r.width(), r.height()); view.scale = target / maxr; clear_trails(); }
    else if(k=='A'){ adaptiveDt=!adaptiveDt; }
    else if(k=='S'){ autoScale=!autoScale; }
    else if(k=='E'){ pp.eps *= 1.2; }
    else if(k=='D'){ pp.eps /= 1.2; }
    else if(k=='O'){ posRand += 0.01; }
    else if(k=='P'){ posRand = std::max(0.0, posRand - 0.01); }
    else if(k=='K'){ velRand += 0.01; }
    else if(k=='L'){ velRand = std::max(0.0, velRand - 0.01); }
    else if(k=='N'){ rngSeed = rngSeed*1664525u + 1013904223u; reset(); }
    else if(k=='B'){ rngSeed ^= (unsigned)tick; }
    else if(k=='T'){ looseTheta += 0.01; if(preset==Preset::FigureEightLoose) reset(); }
    else if(k=='G'){ looseTheta -= 0.01; if(preset==Preset::FigureEightLoose) reset(); }
    else if(k=='Y'){ loosePosScale += 0.01; if(preset==Preset::FigureEightLoose) reset(); }
    else if(k=='H'){ loosePosScale = std::max(0.5, loosePosScale - 0.01); if(preset==Preset::FigureEightLoose) reset(); }
    else if(k=='U'){ looseVelScale += 0.01; if(preset==Preset::FigureEightLoose) reset(); }
    else if(k=='J'){ looseVelScale = std::max(0.5, looseVelScale - 0.01); if(preset==Preset::FigureEightLoose) reset(); }
}

void App::on_wheel(short d, int x, int y){ double s = d>0?1.1:1.0/1.1; view.ox = x + (view.ox - x)*s; view.oy = y + (view.oy - y)*s; view.scale *= s; clear_trails(); }

void App::on_mouse_down(int x, int y){ if(followCOM) return; dragging=true; last={x,y}; clear_trails(); SetCapture(hwnd); }

void App::on_mouse_move(int x, int y, WPARAM flags){ if(followCOM) return; if(dragging && (flags & MK_LBUTTON)){ view.ox += x - last.x; view.oy += y - last.y; last={x,y}; }}

void App::clear_trails(){ for(int i=0;i<3;++i) trails[i].pts.clear(); }